
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.Runtime.Serialization;
using System.Text;
using System.Xml;
using Swaf.BizObj;
using Swaf.BizObj.basic;
using Swaf.Call;
using Swaf.Config;
using Swaf.Container;

namespace Swaf.Security
{
	/// <summary>
	/// The SecurityPrincipalBase class implements all of the common functionality for 
	/// SecurityPrincipals and serves as the base class for the DefaultSecurityPrincipal 
	/// and SecurityPrincipal classes.  The following methods and properties of the 
	/// ISecurityPrincipal interface are implemented in the SecurityPrincipalBase class: 
	/// resourceSets, roles, rolesCall, userName, additionalInfo, isUersInRole, and assert.
	/// </summary>
	[Serializable]
	public abstract class SecurityPrincipalBase : ISecurityPrincipal, ISerializable
	{
		protected INameAddressSupport m_resourceSets = new FlexiMap();
		protected INameAddressSupport m_roles = new FlexiMap();
		protected INameAddressSupport m_additionalInfo = null;
		[NonSerialized] protected ICall m_rolesCall = null;
		protected string m_userName="";
		protected string m_xmlNodeName="";
		protected bool m_deepCloneResourceSets=false;
		internal string m_instanceId=BGuid.Guid.NewGuid().ToString();
		/// <summary>
		/// Constructor for instancing a default security principal from code
		/// </summary>
		/// <param name="UserName">The name of the user associated with this principal</param>
		/// <param name="additionalInfo">INameAddressSupport instance containing application 
		/// specific information about the principal</param>
		/// <param name="rolesCall">ICall instance used to retrieve the roles for a principal.</param>
		public SecurityPrincipalBase(string UserName, INameAddressSupport additionalInfo, ICall rolesCall)
		{
			m_xmlNodeName="SecurityPrincipal";
			m_userName = UserName;
			m_additionalInfo = additionalInfo;
			m_rolesCall=rolesCall;
			if (m_rolesCall != null)
				m_roles = (INameAddressSupport) m_rolesCall.call(this);
			copyResourceSets();
		}

		public bool deepCloneResourceSets
		{
			get{return m_deepCloneResourceSets;}
			set{m_deepCloneResourceSets=value;}
		}

		/// <summary>
		/// Private helper method for copying resourcesets
		/// </summary>
		private void copyResourceSets()
		{
			FlexiMap resSets = (FlexiMap) Application.currentApp.securityMgr.resourceSets;
			foreach(DictionaryEntry  res in resSets)
			{
				IResourceSet resSet = res.Value as IResourceSet;
				if (!m_resourceSets.containsField(resSet.name))
					m_resourceSets.put(resSet.name, resSet.clone(m_deepCloneResourceSets));
			}
			
		}

		/// <summary>
		/// Constructor used by the configuration system or to deserialize a serialize principal from an Xml node.
		/// </summary>
		/// <param name="ele">XmlNode containing information about the principal</param>
		public SecurityPrincipalBase(XmlElement ele)
		{
			m_xmlNodeName=ele.Name;
			m_userName = ele.GetAttribute("userName");
			m_instanceId = ele.GetAttribute("instanceId");
			XmlNode child = ele.SelectSingleNode(".//AdditionalInfo");
			if (child != null)
			{
				foreach(XmlNode node in child.ChildNodes)
				{
					if (node.NodeType == XmlNodeType.Element)
					{
						try
						{
							IBizObjSource src = new FullBizObjSource((XmlElement) node,"");
							IBizObjList lst = src.getList(node.Name,"");
							m_additionalInfo = lst[0];
						}
						catch(Exception)
						{
							m_additionalInfo = new FlexiMap((XmlElement) node);
						}
					}
				}
			}
			
			IApplication app = Application.currentApp;

			XmlNodeList list = ele.SelectNodes(".//ResourceSet");

			if (list != null)
			{
				IResourceSet resSet =null;
				foreach(XmlNode node in list)

				{
					resSet = (IResourceSet) app.factories["ResourceSets"].createFromNickName(node.Name, (XmlElement) node);
					m_resourceSets.put(resSet.name, resSet);
				}
				
			}
			list = ele.SelectNodes(".//Role");
			IRole role=null;
			if (list != null)
			{
				IResourceSet resSet =null;
				foreach(XmlNode node in list)

				{
					//TODO:  We may want to change this to use a classfactory.
					role = new Role((XmlElement) node);
					m_roles.put(role.name, role);
				}
				
			}
			
		}
		#region ISecurityPrincipal Members

		/// <summary>
		/// <see cref="ISecurityPrincipal.rolesCall"/>
		/// </summary>
		public virtual Swaf.Call.ICall rolesCall
		{
			get
			{
				return m_rolesCall;
			}
		}
		
		/// <summary>
		/// <see cref="ISecurityPrincipal.isUserInRole"/>
		/// </summary>
		/// <param name="roleName"></param>
		/// <returns></returns>
		public virtual bool isUserInRole(string roleName)
		{
			return m_roles.containsField(roleName);
		}
		
		/// <summary>
		/// Abstract method for getPrivilege. <see cref="ISecurityPrincipal.getPrivilege"/>
		/// </summary>
		/// <param name="resourceName"></param>
		/// <returns></returns>
		public abstract Privileges getPrivilege(string resourceName);
		public abstract Privileges getPrivilege(string resourceName, bool useDefaultIfNeeded);

		/// <summary>
		/// <see cref="ISecurityPrincipal.hasPrivilege"/>
		/// </summary>
		/// <param name="resourceName"></param>
		/// <returns></returns>
		public virtual bool hasPrivilege(string resourceName)
		{
			return getPrivilege(resourceName) != Privileges.None;
		}
		
		/// <summary>
		/// <see cref="ISecurityPrincipal.assert"/>
		/// </summary>
		/// <param name="resourceName"></param>
		/// <param name="minPrivilege"></param>
		public virtual void assert(string resourceName, Swaf.Security.Privileges minPrivilege)
		{
			if (getPrivilege(resourceName)< minPrivilege)
				throw new AccessDeniedException(this, resourceName);
		}

		/// <summary>
		/// <see cref="ISecurityPrincipal.userName"/>
		/// </summary>
		public virtual string userName
		{
			get
			{
				return m_userName;
			}
		}

	/// <summary>
	/// <see cref="ISecurityPrincipal.resourceSets"/>
	/// </summary>
		public virtual INameAddressSupport resourceSets
		{
			get
			{
				return m_resourceSets;
			}
		}

		/// <summary>
		/// <see cref="ISecurityPrincipal.roles"/>
		/// </summary>
		public virtual INameAddressSupport roles
		{
			get
			{
				return m_roles;
			}
		}

		/// <summary>
		/// <see cref="ISecurityPrincipal.additionalInfo"/>
		/// </summary>
		public virtual INameAddressSupport additionalInfo
		{
			get
			{
				return m_additionalInfo;
			}
		}
		public string instanceId
		{
			get
			{
				return m_instanceId;
			}
		}

		#endregion

		#region INameAddressSupport Members

		/// <summary>
		/// The isGetOnly property is inherited from the INameAddressSupport interface and 
		/// should always return true for security related components that implement the 
		/// INameAddressSupport interface.
		/// <seealso cref="Swaf.Container.INameAddressSupport.isGetOnly"/>
		/// </summary>
		public virtual bool isGetOnly
		{
			get
			{
				return true;
			}
		}

		/// <summary>
		/// <see cref="Swaf.Container.INameAddressSupport.SerializationSupport"/>
		/// </summary>
		public virtual Swaf.Container.XmlSerializationSupport SerializationSupport
		{
			get
			{
				return XmlSerializationSupport.AsElement;
			}
		}

		/// <summary>
		/// The put method is inherited from the INameAddressSupport interface and should 
		/// not be implemented as it breaks security standards to allow editing of the 
		/// current principal once it is created.
		/// </summary>
		/// <param name="nameAddress"></param>
		/// <param name="val"></param>
		/// <param name="extraInfo"></param>
		public virtual void put(string nameAddress, object val, object extraInfo)
		{
			// Do nothing
		}

		/// <summary>
		/// The put method is inherited from the INameAddressSupport interface and should 
		/// not be implemented as it breaks security standards to allow editing of the 
		/// current principal once it is created.
		/// </summary>
		/// <param name="nameAddress"></param>
		/// <param name="val"></param>
		public virtual void put(string nameAddress, object val)
		{
			// Do nothing
		}

		/// <summary>
		/// The containsField method is inherited from the INameAddressSupport interface and 
		/// should net the same result as calling hasPrivilege.
		/// </summary>
		/// <param name="nameAddress">Resource being sought</param>
		/// <returns></returns>
		public virtual bool containsField(string nameAddress)
		{
			INameAddressSupport ns = m_resourceSets.get(NameAddress.getFirstAddress(nameAddress)) as INameAddressSupport;
			if (ns == null)
				return false;
			return ns.containsField(NameAddress.getRemainingAddress(nameAddress));
		}

		/// <summary>
		/// The get method is inherited from the INameAddressSupport interface and should 
		/// net the same result as calling getPrivileges when called with 2 parameters 
		/// (nameaddress and null).  However, if simply called with the name address, get 
		/// should return the actual resource.
		/// </summary>
		/// <param name="nameAddress">Resource being sought</param>
		/// <returns></returns>
		public virtual object get(string nameAddress)
		{
			if (nameAddress == null || nameAddress.Length==0 )
				return this;
			else
				return m_resourceSets.get(nameAddress);
		}

		
		/// <summary>
		/// The get method is inherited from the INameAddressSupport interface and should 
		/// net the same result as calling getPrivileges when called with 2 parameters 
		/// (nameaddress and null).  However, if simply called with the name address, get 
		/// should return the actual resource.
		/// </summary>
		/// <param name="nameAddress">Resource being sought</param>
		/// <param name="oneArg"></param>
		/// <returns></returns>
		public virtual object get(string nameAddress, object oneArg)
		{
			if (nameAddress == null || nameAddress.Length==0 )
			{
				return this;
			}
			else
			{
				return getPrivilege(nameAddress);
			}
		}

		
		/// <summary>
		/// The get method is inherited from the INameAddressSupport interface and should 
		/// net the same result as calling getPrivileges when called with 2 parameters 
		/// (nameaddress and null).  However, if simply called with the name address, get 
		/// should return the actual resource.
		/// </summary>
		/// <param name="nameAddress">Resource being sought</param>
		/// <param name="infoArgs"></param>
		/// <returns></returns>
		public virtual object get(string nameAddress, params object[] infoArgs)
		{
			if (nameAddress == null || nameAddress.Length==0 )
			{
				return this;
			}
			else
			{
				return getPrivilege(nameAddress);
			}
		}

		
		/// <summary>
		/// The getXml method is inherited from the INameAddressSupport interface and returns 
		/// an Xml string representing the serialized information about the current principal, 
		/// its roles and resourceSets.  This method takes a single string parameter that is 
		/// not used by the base implementation.
		/// </summary>
		/// <param name="options">not used by the base implementation</param>
		/// <returns></returns>
		public virtual string getXml(string options)
		{
			StringBuilder buffer = new StringBuilder();
			buffer.Append(string.Format("<{0} userName='{1}' instanceId='{2}'>",m_xmlNodeName ,m_userName, m_instanceId));
			if (m_additionalInfo != null)
				buffer.Append(string.Format("	<AdditionalInfo>{0}</AdditionalInfo>", m_additionalInfo.Xml));
			if (m_resourceSets != null)
			{
				buffer.Append("	<ResourceSets>");
				FlexiMap resourcesets = (FlexiMap) m_resourceSets;
				foreach(DictionaryEntry  res in resourcesets)
				{
					IResourceSet resSet = res.Value as IResourceSet;
					buffer.Append(resSet.Xml);
				}
				buffer.Append("	</ResourceSets>");
			}
			if (m_roles != null)
				buffer.Append(string.Format("	<Roles>{0}</Roles>", m_roles.Xml));
			buffer.Append(string.Format("</{0}>", m_xmlNodeName));
			return buffer.ToString();
		}

		
		/// <summary>
		/// The Xml property is inherited from the INameAddressSupport interface and returns 
		/// the same thing as getXml.
		/// </summary>
		public virtual string Xml
		{
			get
			{

				return getXml("");
			}
		}

		#endregion
		public SecurityPrincipalBase(System.Runtime.Serialization.SerializationInfo info, StreamingContext context)
		{
			m_instanceId = info.GetString("id");
			m_userName=info.GetString("user");
			m_roles = (FlexiMap)info.GetValue("roles", typeof(FlexiMap));
			m_resourceSets = (FlexiMap)info.GetValue("resources", typeof(FlexiMap));
			string addInfoType =null;
			try
			{
				addInfoType = info.GetString("addInfoType");
				if (addInfoType != null)
				{
					Type addInfo = Type.GetType(addInfoType);
					m_additionalInfo = (INameAddressSupport)info.GetValue("addInfo", addInfo);
				}
			}
			catch(Exception){}
			m_rolesCall = Application.currentApp.securityMgr.defaultRolesCall;
		}
		#region ISerializable Members

		public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, StreamingContext context)
		{
			info.AddValue("id",m_instanceId);
			info.AddValue("user", m_userName);
			info.AddValue("roles", m_roles);
			info.AddValue("resources", m_resourceSets);
			if (m_additionalInfo != null)
			{
				info.AddValue("addInfoType", m_additionalInfo.GetType().ToString());
				info.AddValue("addInfo", m_additionalInfo);
			}
		}

		#endregion

		public IEnumerator GetEnumerator() { return m_resourceSets.GetEnumerator(); }
	}
}
